NGNIX Guide

This is an expanded updated version of my original HTTPS guide, but more detailed after learning more info. If you wanna read the old version, click here. I created the original HTTPS page when I didn't properly know how to do so. Developing my own web server SpyderFly (still a work in progress) made me realize I need to overhaul this guide.

So what is NGINX? It's how many websites run. NGINX is used by companies Netflix, uk.gov sites, TikTok, and others. Cloudflare used to, but now uses its own web server called Pingora. Started by Igor Sysoev in 2002.

image courtesty of Igor Sysoev

It took him two years of development time before he released it in 2004. He created it after having issues with the main web server at the time Apache. Apache is still used today, but it's considered older.

Disclaimer

This guide assumes you already own a domain name from somewhere like Cloudflare and have your domain's DNS pointed to your public IP. This also assumes you have port 80 & 443 open on your router. If you have not done these, please do so now.

This is a bigger page so here's some navigation you might want

  • Install
  • Basic Config
  • URI
  • Auto Index
  • PHP
  • HTTPS
  • Proxy
  • Net Proxy
  • Other Config
  • Install

    To install, run sudo apt install nginx -y Once install is complete, head to your LAN IP in your browser. No port number, just your LAN IP. When you don't specify a port number, it defaults to port 80. Standard web traffic operates on a combination of port 80 & 443. I'll get into 443 later. When you go to the LAN IP, you should see this:

    Run in your terminal sudo nginx -t to test the config. Make sure to memorize this command, you'll need it. it runs through NGINX's config file. The config file is dynamic based on the OS. If you would like to read the config, run nano /etc/nginx/nginx.conf Most of NGINX's stuff is stored in /etc/nginx/ The binary is in /usr/sbin/nginx the background system is /usr/lib/systemd/system/nginx.service the PID config is in /run/nginx.pid and the log files are in /var/nginx/ There's probably ones I'm forgetting.

    Creating Config

    NGINX handles its sites with the directory /etc/nginx/sites-enabled/ One thing about NGINX is that if the test fails, the whole web server goes down. That's a major flaw I have with it and why I started SpyderFly. You don't create your site config in the enabled sites, you create it in /etc/nginx/sites-available/ Create your config there. Once you do, run sudo ln -s /etc/nginx/sites-available/config_name /etc/nginx/sites-enabled/ and then run sudo nginx -t If it succeeds, run sudo systemctl reload nginx If not, remove the enabled config by running rm /etc/nginx/sites-enabled/config_name This keeps your config in the available sites but puts it away when it doesn't work. No matter what config you make, these steps are the same for this. When we get into certbot, it'll be different. I will not repeat these steps shown here to avoid redundancy.

    Basic config

    Let's start with a very simple config. NGINX's config is essentially its own coding language. The coding language I'm most familiar with is C. In C, you need sqiggly brackets if you're creating a closed case. If it's one command, it's a semicolon at the end. But NGINX also ignores pound signs just like Python. That logic is how NGINX works. Here's a basic config:

    server {
      listen 4674;
      root /var/www/html/;
    }
              
    The server part contains the main config. Listen is the port number it listens on. Root is the directory the site will operate on. This config is a little basic. CSS and images don't work, and sub-directories (uri) does not work. Let's add a little more detail to the config:
    server {
      listen 4674;
      root /var/www/html/;
    
      index index.html;
    }
              
    The purpose of adding the index is to have NGINX know where to look. I figured it was to know where to look for HTML files that weren't named index.html, but it already works for that.

    Uri

    Uri stands for Uniform Resource Identifier. It's what identifies sub directories. Like how my domain is on my home page, this guide is a sub-directory. This guide is a URI.

    To add the feature to your config, add a location config:

    server {
      listen 4674;
      root /var/www/html/;
    
      index index.html;
    
      location / {
        try_files $uri $uri/ =404;
      }
    }
              
    The uri tells it where to look and try.

    Auto Index

    You may have gone to a sub folder or part of your NGINX site and saw this:

    That means your folder does not have an index file. Two ways to solve this: add one, or turn on auto index. Let's update the config to this:

    server {
      listen 4674;
      root /var/www/html/;
    
      index index.html;
    
      location / {
        try_files $uri $uri/ =404;
        autoindex on;
      }
    }
              
    This gives an autogenerated index of your folder and what's inside. For the most part, I keep it on.

    PHP

    Honestly, I know more about Java than PHP. PHP--other than assembly--is like the main language I do not know. So I'm not going to explain what PHP is. If your site is going to use it, you need to add this. At the time of me writing this section (creation date), today was a massive PHP security breach. If you're going to use it in NGINX, it needs to have a few things. First, lets update the index:

    server {
      listen 4674;
      root /var/www/html/;
    
      index index.html index.php;
    
      location / {
        try_files $uri $uri/ =404;
        autoindex on;
      }
    }
              
    Then, add the proper location config for PHP:
    server {
      listen 4674;
      root /var/www/html/;
    
      index index.html index.php;
    
      location / {
        try_files $uri $uri/ =404;
        autoindex on;
      }
    
      location ~ \.php$ {
        include snippets/fastcgi-php.conf;
    
      # With php-fpm (or other unix sockets):
      #  fastcgi_pass unix:/run/php/php8.5-fpm.sock;  # change this
      # With php-cgi (or other tcp sockets):
      #  fastcgi_pass 127.0.0.1:9000;
      }
    }
              
    The part where I said # change this lemme explain. PHP is a bit weird with how it works. If you're going to install it, do the sock version. Or I just call it the standalone version. Run sudo apt install php-fpm to install. After install, run php -v to get your version. Ok, my server says 8.4, but I swear it was 8.5. Ah! Because during this guide as I was testing things, I accidentally install the CLI version. No, you want the fpm one. That for me is 8.5. So change your config to that and remove the # sign. Also, don't include the minor version. Just 8.5, not 8.5.12.

    HTTPS

    Remember me mentioning port 443? I don't, I almost forgot. HTTPS is the encryption of traffic and decryption. It requires both 80 and 443 to be port forwarded to work. Imagine it like a lawn mower. You don't have to keep pulling the start rope to keep it running, you just need it to jumpstart and the mower does the rest. So if you have not port forwarded 80 & 443, please do so now. Once you have, you'll need certbot. Run sudo apt install certbot python3-certbot-nginx -y to install certbot for NGINX. Once you have done so, you need to edit your config. Remove the listen line, and add a line under root called "server name".

    server {
      root /var/www/html/;
      server_name example.domain.com;
    
      index index.html index.php;
    
      location / {
        try_files $uri $uri/ =404;
        autoindex on;
      }
    
      location ~ \.php$ {
        include snippets/fastcgi-php.conf;
    
      # With php-fpm (or other unix sockets):
        fastcgi_pass unix:/run/php/php8.5-fpm.sock;
      # With php-cgi (or other tcp sockets):
      #  fastcgi_pass 127.0.0.1:9000;
      }
    }
            
    You can have the server name in a non-https config, it just would need to be server_name _; Once you add your domain name, test, and reload. run sudo certbot --nginx -d example.domain.com to deploy your certificate. run nano /etc/nginx/sites-enabled/config_name and you should see something like this:
    server {
      root /var/www/html/;
      server_name example.domain.com;
      index index.html index.php;
    
      location / {
        autoindex on;
        try_files $uri $uri/ =404;
      }
    
      location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/run/php/php8.5-fpm.sock;
      }
    
      listen [::]:443 ssl; # managed by Certbot
      listen 443 ssl; # managed by Certbot
      ssl_certificate /etc/letsencrypt/live/example.domain.com/fullchain.pem; # managed by Certbot
      ssl_certificate_key /etc/letsencrypt/live/example.domain.com/privkey.pem; # managed by Certbot
      include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
      ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
    
    }
    
    # I'll explain this part
    server {
      if ($host = example.domain.com) {
        return 301 https://$host$request_uri;
      } # managed by Certbot
    
      listen 80;
      listen [::]:80;
    
      server_name example.domain.com;
      return 404; # managed by Certbot
    }
            
    The part I said I'll explain. This is generated and how it creates the HTTP proxy. I have little clue how it works, maybe I'll have a better clue when I get farther on SpyderFly. This is generated by certbot. If you do not remove the listen port at the top, this will not generate. Commenting it out will also prevent it from generating. You can add it back afterwards if you desire though. If you go to your domain and this didn't generate, you'll get a security notice. You'll be fine once you add it, reload, and clear your browser cache.

    Once you go to your domain in the browser, you should see this icon next to your domain and the URL should be https.

    Proxy

    A proxy is HTTPS, but it's not the same as before. Instead of having a domain link directly to a directory like the previous HTTPS example, this links a domain to a pre-existing port process. Most likely you are using this for Jellyfin. Proxy configs are much more advanced versions of the standard web directory. You are building the web server with the web directory. You are passing cards in hand to another web server with a proxy.

    Jellyfin

    Let's show how to do it with Jellyfin as an example. Because the web server is already running, most of the work is done. And Jellyfin does have its own NGINX proxy guide here. However, I think it's a little confusing and in testing I found it to not work. (Few mintues after updating this part, I think it was because I forgot to properly add the domain. I'll check it out again later) Jellyfin's version frames it as if the certs are already there anyways. we won't do that. Here's a cleaned up version of the config I use for my Jellyfin:

    server {
      server_name jellyfin.example.org;
    
    location / {
        proxy_pass http://127.0.0.1:8096;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Protocol $scheme;
        proxy_set_header X-Forwarded-Host $http_host;
        proxy_buffering off;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Authorization $auth_header_fallback;
        proxy_set_header X-Emby-Authorization $x_emby_fallback;
        proxy_set_header X-Emby-Token $http_x_emby_token;
      }
    }
              
    Just replace the jellyfin.example.org with you real domain. Then run sudo nginx -t If sucessful, then run sudo certbot --nginx -d jellyfin.example.org and if that suceeds, run sudo systemctl reload nginx to finalize it. These steps apply the same for every NGINX proxy config you encounter, just different config. After you run certbot and check your config again, it should look something like this:
    server {
      server_name jellyfin.example.org;
    
      location / {
        proxy_pass http://127.0.0.1:8096;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Protocol $scheme;
        proxy_set_header X-Forwarded-Host $http_host;
        proxy_buffering off;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Authorization $auth_header_fallback;
        proxy_set_header X-Emby-Authorization $x_emby_fallback;
        proxy_set_header X-Emby-Token $http_x_emby_token;
      }
    
        listen 443 ssl; # managed by Certbot
        ssl_certificate /etc/letsencrypt/live/jellyfin.example.org/fullchain.pem; # managed by Certbot
        ssl_certificate_key /etc/letsencrypt/live/jellyfin.example.org/privkey.pem; # managed by Certbot
        include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
        ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
    
    }
    
    server {
        if ($host = jellyfin.example.org) {
            return 301 https://$host$request_uri;
        } # managed by Certbot
    
        listen 80;
        server_name jellyfin.example.org;
        return 404; # managed by Certbot
    }
              
    That's how you create a proxy. It depends on the software you're proxying, but I've found this same config to work for Sonarr, Code Server, Qbittorrent, and probably some others I'm forgetting.

    Net Proxy

    I think the correct term is "reverse proxy". I found out about this today (October 28th, 2026). I met up with someone and talked Linux and he mentioned a reverse proxy. I'd heard about it, and then I realized I should add this to my server guide. The purpose of this proxy is to allow accessing devices on the network off the network. I haven't tested it yet, so I'm leaving the config here:

    server {
      listen 80;
      server_name yourdomain.com;
    
      location / {
        proxy_pass http://192.168.1.104:8080;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
      }
    }
              

    LEMP Stack

    I'm not going to get into too much detail here. A LEMP stack is hyper-specific and the web server part is minor compared to the rest. LEMP stands for

  • Linux
  • Engine-X (NGINX)
  • MySQL/MariaDB
  • PHP
  • Linux is the OS, NGINX is the web server, MySQL is the database, and PHP is the web code. It's a very hyper-specific way to create a web software. A lot of sites like WordPress use a LEMP stack. Most self hosting software do not use a LEMP stack because of how complicated they are. The only one I know is Nextcloud LEMP Stacks. It's similar to a proxy, but where NGINX is the web server you are parsing it to. I barley understand it. I've done it for Nextcloud. Tempted to do another. Haven't done one since I wrote my LEMP Stacks guide. Unless you want a challenge, I do not recommend you actually do one.

    Other Config

    Some info I wanna mention that I can't figure out where to put.

    IPV6

    I've been server hosting for over a year now, I still don't know what ipv6 is. In an NGINX config, if you see listen [::]:80; or similar with a different port, it's ipv6.

    HTM

    HTM is an older file type left over from the days of MS DOS. If you look at a default config for NGINX, youll see the index portion looking like this: index index.html index.htm index.nginx-debian.html; As I covered in the PHP section, you can add to it. Post install, you can remove the index.nginx-debian.html The HTM part is something you will likely never need so you can remove that too.

    Quick Navigation

  • Prerequisites
  • Configuring the OS
  • Minecraft Server
  • Minecraft JDS & BDS
  • Jellyfin
  • Nextcloud
  • Nextcloud LEMP stacks
  • Tailscale
  • Port Forwarding
  • HTTPS Configuration
  • Torrenting
  • Coding
  • SpyderFly
  • Recommended Tools
  • Back To Homepage

    Other info

    Any issues with this guide, shoot me an email "[email protected]"

    Date created: August 7th, 2026

    Last modified: November 12th, 2026

    this site is open source